//
//  HLEM3U8WorkFlow.m
//  DownloadM3U8Tool_Example
//
//  Created by hle1879 on 2020/8/28.
//  Copyright © 2020 134344. All rights reserved.
//

#import "HLEM3U8WorkFlow.h"
#import <AFNetworking/AFNetworking.h>
#import "HLEM3u8DownloadManager.h"
#import <mobileffmpeg/mobileffmpeg.h>

@interface HLEM3U8WorkFlow ()

@property(nonatomic,strong)NSURL *workSpace;
@property (nonatomic,strong,readwrite)HLEM3U8DownloadTask *task;
@property(nonatomic,strong)NSMutableArray *tsWaitingDownloadFiles;
@property(nonatomic,strong)NSOperationQueue *operationQueue;
@property(nonatomic,strong)dispatch_queue_t dispatchQueue;
@property(nonatomic,strong)NSTimer *downloadTimer;
@property(nonatomic,assign)NSInteger preCompletedCount;//记录前1s前已经下载的数据大小
@property(nonatomic,assign)NSInteger completedCount;//记录已经完成的数据大小
@property(nonatomic,strong)NSMutableDictionary<NSNumber*,NSProgress*> *progressDictionary;
@property(nonatomic,strong)NSURLSessionDownloadTask *currentRequest;
@property(nonatomic,strong) NSFileManager *fileManager;
@property(nonatomic,strong)AFHTTPSessionManager *manager;

@property(nonatomic,assign)NSInteger downloadRetries;//重试下载的次数

@property(nonatomic,copy)void (^downloadProgressBlock)(HLEM3U8DownloadTask *,NSProgress *, NSInteger);//下载进度
@property(nonatomic,copy)void (^downloadSpeedBlock)(HLEM3U8DownloadTask *,NSInteger);//下载速度
@property(nonatomic,copy)void (^downloadSuccessBlock)(HLEM3U8DownloadTask *);//下载成功
@property(nonatomic,copy)void (^downloadFailureBlock)(HLEM3U8DownloadTask *,NSError *);//下载失败

@property(nonatomic,copy)void (^combineSuccessBlock)(HLEM3U8DownloadTask *);//合并成功
@property(nonatomic,copy)void (^combineFailureBlock)(HLEM3U8DownloadTask *,NSError *);//合并失败

@end

@implementation HLEM3U8WorkFlow

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.operationQueue = [[NSOperationQueue alloc]init];
        self.operationQueue.maxConcurrentOperationCount = 1;
        self.operationQueue.qualityOfService = NSQualityOfServiceUtility;
        self.dispatchQueue = dispatch_queue_create("com.golvin.HLEM3U8", DISPATCH_QUEUE_CONCURRENT);
        self.progressDictionary = NSMutableDictionary.new;
        self.workSpace = [NSURL fileURLWithPath:NSHomeDirectory()];
        self.fileManager =  [NSFileManager defaultManager];
        self.manager = HLEM3u8DownloadManager.standardUserDefaults.manager;
    }
    return self;
}



- (BOOL)initExitTask:(HLEM3U8DownloadTask*)task{
    _task = task;
    if (self.task == nil || self.task.url == nil || self.task.uri == nil || self.task.taskID < 0) {
        return NO;
    }
    self.operationQueue.suspended = NO;
    return YES;
}

- (void)initTask:(HLEM3U8DownloadTask*)task success:(nullable void (^)(HLEM3U8DownloadTask *task))success
         failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    _task = task;
    if (self.task == nil || self.task.url == nil || self.task.uri == nil || self.task.taskID < 0) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEParametersInvalid userInfo:@{NSLocalizedDescriptionKey:@"参数非法"}];
        if (failure != nil) {
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        return;
    }
    
    self.operationQueue.suspended = YES;
    BOOL isDir = NO;
    NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
    BOOL isExist= [self.fileManager fileExistsAtPath:workDir.path isDirectory:&isDir];
    if (!isDir || !isExist) {
        NSError *error;
        [self.fileManager createDirectoryAtURL:workDir withIntermediateDirectories:YES attributes:nil error:&error];
        if (error) {
            [self.operationQueue cancelAllOperations];
            if (failure != nil) {
                error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEHandleCacheFailed userInfo:@{NSLocalizedDescriptionKey:@"创建缓存目录失败"}];
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    failure(self.task,error);
                });
            }
            return;
        }
    }
    
    NSURL *m3u8Dir = [workDir URLByAppendingPathComponent:@"m3u8"];
    if ([self.fileManager fileExistsAtPath:m3u8Dir.path]) {
        //如果保存的缓存文件已经存在
        NSData *data = [[NSData alloc]initWithContentsOfURL:m3u8Dir];
        HLEM3U8DownloadTask *localTask = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        [self.task  copyPropertys:localTask];
        self.operationQueue.suspended = NO;
        if (success) {
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                success(self.task);
            });
        }
    }else{
        //如果没有缓存文件，则需要先下载m3u8文件，并且解析后写入缓存
        NSURL *destinationURL = [workDir URLByAppendingPathComponent:@"file.m3u8"];
        NSURLRequest *request = [NSURLRequest requestWithURL:self.task.url];
        NSURLSessionDownloadTask *downloadTask = [self.manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
        } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
            return [NSURL fileURLWithPath:destinationURL.path];
        } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
            if (error != nil) {
                if (failure != nil) {
                    error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEM3uFileDownloadError userInfo:@{NSLocalizedDescriptionKey:@"下载m3u8文件失败"}];
                    failure(self.task,error);
                }
            }else{
                [self m3uDownloadDidFinished:destinationURL success:success failure:failure];
            }
        }];
        [downloadTask resume];
    }
}


- (void)downloadWithProgress:(void (^)(HLEM3U8DownloadTask *task,NSProgress *progress, NSInteger allSize))progress
                       speed:(void (^)(HLEM3U8DownloadTask *task,NSInteger speed))speed
                     success:(nullable void (^)(HLEM3U8DownloadTask *task))success
                     failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    self.downloadProgressBlock = progress;
    self.downloadSpeedBlock = speed;
    self.downloadSuccessBlock = success;
    self.downloadFailureBlock = failure;
    if (self.task == nil || self.task.taskID < 0 || self.task.url == nil || self.task.uri == nil) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEParametersInvalid userInfo:@{NSLocalizedDescriptionKey:@"当前任务非法"}];
        if (failure != nil) {
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, self.task.mutableCopy)
        return;
    }
    //判断缓存目录是否存在
    NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
    BOOL isDir = NO;
    BOOL isExist= [self.fileManager fileExistsAtPath:workDir.path isDirectory:&isDir];
    if (!isDir || !isExist) {
        NSError *error;
        [self.fileManager createDirectoryAtURL:workDir withIntermediateDirectories:YES attributes:nil error:&error];
        if (error) {
            [self.operationQueue cancelAllOperations];
            if (failure != nil) {
                error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEHandleCacheFailed userInfo:@{NSLocalizedDescriptionKey:@"创建缓存目录失败"}];
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    failure(self.task,error);
                });
            }
            MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, self.task.mutableCopy)
            return;
        }
    }
    //下载ts
    __weak typeof(self) weakSelf = self;
    [self.operationQueue addOperationWithBlock:^{
        __strong typeof(weakSelf) self = weakSelf;
        self.operationQueue.suspended = YES;
        if (self.task.tsArr.count == 0) {
            NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEM3uFileContentInvalid userInfo:@{NSLocalizedDescriptionKey:@"m3u8文件内容错误"}];
            if (failure != nil) {
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    failure(self.task,error);
                });
            }
            MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, self.task.mutableCopy)
            return;
        }
        self.tsWaitingDownloadFiles = [NSMutableArray arrayWithArray:self.task.tsArr];
        [self downloadNextFile];
        [self createTimer];
    }];
}


- (void)pause{
    [self.operationQueue cancelAllOperations];
    [self destroyTimer];
    [self.currentRequest cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
    }];
    [self.progressDictionary removeAllObjects];
    [self.tsWaitingDownloadFiles removeAllObjects];
    self.completedCount = 0;
    self.preCompletedCount = 0;
    self.operationQueue.suspended = NO;
    self.currentRequest = nil;
    self.downloadProgressBlock = nil;
    self.downloadSpeedBlock = nil;
    self.downloadSuccessBlock = nil;
    self.downloadFailureBlock = nil;
}

- (BOOL)cancel{
    [self pause];
    //删除下载的目录
    NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
    BOOL isDir = NO;
    BOOL isExist= [self.fileManager fileExistsAtPath:workDir.path isDirectory:&isDir];
    if (isDir && isExist) {
        NSError *error;
        [self.fileManager removeItemAtURL:workDir error:&error];
        if (error != nil) {
            return NO;
        }
    }
    return YES;
}


-(void)combineMP4WithFilePath:(NSString*)filepath success:(nullable void (^)(HLEM3U8DownloadTask *task))success failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure complete:(nonnull void (^)(void))complete{
    self.combineSuccessBlock = success;
    self.combineFailureBlock = failure;
    if (self.task == nil || self.task.url == nil || self.task.uri == nil || self.task.taskID < 0 || filepath == nil || filepath.length == 0) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEParametersInvalid userInfo:@{NSLocalizedDescriptionKey:@"参数非法"}];
        if (failure != nil) {
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,self.task.mutableCopy)
        complete();
        return;
    }
    NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
    NSURL *mp4Url = [self.workSpace URLByAppendingPathComponent:filepath];
    NSURL *saveMp4Url = [NSURL fileURLWithPath:[mp4Url.path stringByDeletingLastPathComponent]];
    NSString *combineTsName = [NSString stringWithFormat:@"%@.ts",self.task.name];
    NSURL *combineTsUrl = [workDir URLByAppendingPathComponent:combineTsName];
    BOOL isDir = NO;
    BOOL isExist= [self.fileManager fileExistsAtPath:workDir.path isDirectory:&isDir];
    if (!isExist || !isDir) {
        if (failure != nil) {
            NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEHandleCacheFailed userInfo:@{NSLocalizedDescriptionKey:@"ts文件缓存目录未存在"}];
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,self.task.mutableCopy)
        complete();
        return;
    }
    isExist= [self.fileManager fileExistsAtPath:saveMp4Url.path isDirectory:&isDir];
    if (!isExist || !isDir) {
        NSError *error;
        [self.fileManager createDirectoryAtURL:saveMp4Url withIntermediateDirectories:YES attributes:nil error:&error];
        if (error) {
            if (failure != nil) {
                error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEHandleCacheFailed userInfo:@{NSLocalizedDescriptionKey:@"创建缓存目录失败"}];
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    failure(self.task,error);
                });
            }
            MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,self.task.mutableCopy)
            complete();
            return;
        }
    }
    isExist= [self.fileManager fileExistsAtPath:combineTsUrl.path isDirectory:&isDir];
    if (!isExist || isDir) {
        [self.fileManager createFileAtPath:combineTsUrl.path contents:nil attributes:nil];
    }
    
    isExist= [self.fileManager fileExistsAtPath:mp4Url.path isDirectory:&isDir];
    if (isExist && !isDir) {
        NSError *error;
        [self.fileManager removeItemAtURL:mp4Url error:&error];
        if (error) {
            if (failure != nil) {
                NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEHandleCacheFailed userInfo:@{NSLocalizedDescriptionKey:@"删除缓存失败"}];
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    failure(self.task,error);
                });
            }
            MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,self.task.mutableCopy)
            complete();
            return;
        }
    }
    
    NSMutableArray *tsFilePaths = NSMutableArray.new;
    NSInteger index = 1;
    for (NSURL *ts in self.task.tsArr) {
        NSURL *tsUrl = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%zd.%@",workDir.path,index,[ts pathExtension]]];
        if ([self.fileManager fileExistsAtPath:tsUrl.path]){
            [tsFilePaths addObject:tsUrl];
        }else{
            break;
        }
        index++;
    }
    if (self.task.tsArr.count < tsFilePaths.count) {
        //没有下载完成
        if (failure) {
            NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLECombineTsToMP4NoFinish userInfo:@{NSLocalizedDescriptionKey:@"文件还未下载完成不能进行合并"}];
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,self.task.mutableCopy)
        complete();
        return;
    }
    
    dispatch_async(self.dispatchQueue, ^{
        NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:combineTsUrl.path];
        for (NSURL *tsFilePath in tsFilePaths) {
            if ([self.fileManager fileExistsAtPath:tsFilePath.path]) {
                NSData *data = [NSData dataWithContentsOfURL:tsFilePath];
                [fileHandle writeData:data];
            }
        }
        [fileHandle closeFile];
        
        //进行合并
        NSString *command = [NSString stringWithFormat:@"-y -i %@ -acodec copy -vcodec copy -f mp4 %@",combineTsUrl.path,mp4Url.path];
        //NSString *command = [NSString stringWithFormat:@"-y -i %@ -acodec copy -vcodec h264 -f mp4 %@",combineTsUrl.path,mp4Url.path];
        NSInteger flag = [MobileFFmpeg execute:command];
        
        
        //删除合并的ts文件
        NSError *error;
        [self.fileManager removeItemAtURL:combineTsUrl error:&error];
        if (flag == RETURN_CODE_SUCCESS) {
            ///删除缓存目录
            NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
            NSURL *filem3u8Url = [workDir URLByAppendingPathComponent:@"file.m3u8"];
            NSURL *m3u8DataUrl = [workDir URLByAppendingPathComponent:@"m3u8"];
            [self.fileManager removeItemAtURL:filem3u8Url error:&error];
            [self.fileManager removeItemAtURL:m3u8DataUrl error:&error];
            //删除ts缓存目录
            for (NSURL *tsFilePath in tsFilePaths) {
                if ([self.fileManager fileExistsAtPath:tsFilePath.path]) {
                    [self.fileManager removeItemAtURL:tsFilePath error:&error];
                }
            }
            //任务完成
            if ([self.delegate respondsToSelector:@selector(workflowFinish:)]) {
                [self.delegate workflowFinish:self];
            }
            if (success != nil) {
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    success(self.task);
                })
            }
            MAIN_POST_NOTIFICATION(HLECombineMP4CompleteNotice,self.task.mutableCopy)
        }else{
            if (failure) {
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLECombineTsToMP4Error userInfo:@{NSLocalizedDescriptionKey:@"ts转mp4失败"}];
                    failure(self.task,error);
                });
            }
            MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,self.task.mutableCopy)
        }
        complete();
    });
    
}

///私有方法
///下载完成
-(void)taskDidComplete:(NSURLSession * _Nullable)session task:(NSURLSessionTask * _Nonnull)task error:(NSError * _Nullable)error{
    NSString *downloadPath = [task.currentRequest.URL absoluteString];
    if ([downloadPath hasSuffix:@".ts"]) {
        NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
        NSInteger index = [self.task.tsArr indexOfObject:task.currentRequest.URL];
        NSString *tempFileName = [NSString stringWithFormat:@"%@.tmp",@(index+1).stringValue];
        NSURL *tsLocalTempURL = [workDir URLByAppendingPathComponent:tempFileName];
        [self tsDownloadFinishWithUrl:task.currentRequest.URL tsLocalTempURL:tsLocalTempURL response:task.response error:error];
    }
}


- (void)m3uDownloadDidFinished:(NSURL*)url success:(nullable void (^)(HLEM3U8DownloadTask *task))success  failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    if (url ==  nil) {
        if (failure != nil) {
            NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEM3uFileDownloadError userInfo:@{NSLocalizedDescriptionKey:@"下载m3u8文件失败"}];
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        return;
    }
    
    [self parseM3u:url success:success failure:failure];
}

-(void)parseM3u:(NSURL*)file success:(nullable void (^)(HLEM3U8DownloadTask *task))success  failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    NSError *error;
    if (file == nil) {
        if (failure != nil) {
            error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEM3uFileUrlError userInfo:@{NSLocalizedDescriptionKey:@"要解析的m3u8文件路径错误"}];
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        return;
    }
    NSString *m3uStr = [NSString stringWithContentsOfURL:file encoding:NSUTF8StringEncoding error:&error];
    if (error) {
        if (failure != nil) {
            error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEM3uFileContentInvalid userInfo:@{NSLocalizedDescriptionKey:@"m3u8文件错误"}];
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        return;
    }
    NSArray *tempArr = [m3uStr componentsSeparatedByString:@"\n"];
    NSMutableArray *tsArr = NSMutableArray.new;
    for (NSString *obj in tempArr) {
        if ([obj hasSuffix:@".ts"]) {
            NSString *urlStr = obj;
            if ([urlStr.lowercaseString hasPrefix:@"http://"]||[urlStr.lowercaseString hasPrefix:@"https://"]) {
                [tsArr addObject:[NSURL URLWithString:urlStr]];
            }else{
                [tsArr addObject:[[self.task.url URLByDeletingLastPathComponent] URLByAppendingPathComponent:obj]];
            }
        }
    }
    if (tsArr.count == 0) {
        if (failure != nil) {
            error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEM3uFileContentInvalid userInfo:@{NSLocalizedDescriptionKey:@"m3u8文件内容错误"}];
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                failure(self.task,error);
            });
        }
        return;
    }
    self.task.tsArr = tsArr;
    [self calculateSize:tsArr completion:^(NSInteger totalSize, NSError *error) {
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.task];
        NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
        NSURL *m3u8Dir = [workDir URLByAppendingPathComponent:@"m3u8"];
        if ([self.fileManager fileExistsAtPath:m3u8Dir.path]) {
            [self.fileManager removeItemAtURL:m3u8Dir error:&error];
        }
        [data writeToURL:m3u8Dir atomically:YES];
        self.operationQueue.suspended = NO;
        if (success) {
            __weak typeof(self) weakSelf = self;
            dispatch_main_async_safe(^{
                __strong typeof(weakSelf)self = weakSelf;
                success(self.task);
            });
        }
    }];
}

-(void)calculateSize:(NSArray<NSURL*> *)tsArr completion:(void (^)(NSInteger totalSize,NSError *error))completion{
    __block NSInteger remain = tsArr.count;
    __block NSInteger totalSize = 0;
    __block NSMutableArray *tsSizeArr = NSMutableArray.new;
    for (NSURL *url in tsArr) {
        [self getFileSize:url completion:^(NSInteger size,NSError *error) {
            remain -= 1;
            totalSize += size;
            [tsSizeArr addObject:@(size)];
            if (remain == 0) {
                self.task.totalSize = totalSize;
                self.task.tsSizeArr = tsSizeArr;
                if (completion != nil) {
                    completion(totalSize,nil);
                }
            }
        }];
    }
}

-(void)getFileSize:(NSURL*)url completion:(void (^)(NSInteger size,NSError *error))completion{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"HEAD"];
    NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            if (completion != nil) {
                NSError *err = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEHandleGetTsFileSizeError userInfo:@{NSLocalizedDescriptionKey:@"获取ts文件大小失败"}];
                completion(0,err);
            }
        }
        NSHTTPURLResponse *resp = (NSHTTPURLResponse*)response;
        NSString *length = resp.allHeaderFields[@"Content-Length"];
        NSInteger size = 0;
        if (length != nil) {
            size = length.integerValue;
        }
        if (completion != nil) {
            completion(size,nil);
        }
    }];
    [task resume];
    
}


- (void)createTimer{
    __weak typeof(self)weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        __strong typeof(weakSelf) self = weakSelf;
        if (self.downloadTimer != nil) {
            [self destroyTimer];
        }
        self.downloadTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
    });
}


- (void)timerFire{
    if (self.task.totalSize <= 0) {
        return;
    }
    
    NSProgress *progress = [NSProgress progressWithTotalUnitCount:self.task.totalSize];
    NSInteger completedUnitCount = self.completedCount;
    for (NSProgress *pro in self.progressDictionary.allValues) {
        completedUnitCount += pro.completedUnitCount;
    }
    progress.completedUnitCount = completedUnitCount;
    if (completedUnitCount <= 0) {
        return;
    }
    
    if (self.downloadProgressBlock != nil) {
        __weak typeof(self) weakSelf = self;
        dispatch_main_async_safe(^{
            __strong typeof(weakSelf)self = weakSelf;
            self.downloadProgressBlock(self.task,progress, self.task.totalSize);
        });
    }
    
    NSInteger preCompletedCount = completedUnitCount - self.preCompletedCount;
    if (preCompletedCount < 0) {
        return;
    }
    self.preCompletedCount = completedUnitCount;
    
    if (self.downloadSpeedBlock != nil) {
        __weak typeof(self) weakSelf = self;
        dispatch_main_async_safe(^{
            __strong typeof(weakSelf)self = weakSelf;
            self.downloadSpeedBlock(self.task,preCompletedCount);
        });
    }
}

- (void)destroyTimer{
    if (self.downloadTimer != nil) {
        [self.downloadTimer invalidate];
        self.downloadTimer = nil;
    }
}



- (void)downloadNextFile{
    NSURL *tsURL = self.tsWaitingDownloadFiles.firstObject;
    [self.tsWaitingDownloadFiles removeObjectAtIndex:0];
    NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
    NSString *fileName = [NSString stringWithFormat:@"%@.%@",@(self.task.tsArr.count - self.tsWaitingDownloadFiles.count).stringValue,[tsURL pathExtension]];
    NSString *tempFileName = [NSString stringWithFormat:@"%@.tmp",@(self.task.tsArr.count - self.tsWaitingDownloadFiles.count).stringValue];
    NSURL *tsLocalURL = [workDir URLByAppendingPathComponent:fileName];
    NSURL *tsLocalTempURL = [workDir URLByAppendingPathComponent:tempFileName];
    
    if ([self.fileManager fileExistsAtPath:tsLocalURL.path]) {
        //如果该文件已经下载
        NSError *error;
        NSInteger localSize = [[self.fileManager attributesOfItemAtPath:tsLocalURL.path error:&error] fileSize];
        if (error) {
            //如果获取本地ts文件大小失败
            [self.fileManager removeItemAtURL:tsLocalURL error:&error];
        }else{
            NSProgress *progress = [NSProgress progressWithTotalUnitCount:localSize];
            progress.completedUnitCount = localSize;
            self.completedCount += localSize;
            self.preCompletedCount += localSize;
            if (self.tsWaitingDownloadFiles.count > 0) {
                [self downloadNextFile];
            }else{
                //所有任务都完成
                [self allDownloadsDidFinished];
            }
            return;
        }
    }
    
    NSData *resumeData = nil;
    if ([self.fileManager fileExistsAtPath:tsLocalTempURL.path]) {
        resumeData = [NSData dataWithContentsOfURL:tsLocalTempURL];
    }
    self.preCompletedCount += resumeData.length;
    //判断是否支持断点下载
    if (self.task.supportBrokenPointDownload && resumeData!= nil && resumeData.length > 0) {
        //如果支持断点下载
        __weak typeof(self) weakSelf = self;
        NSURLSessionDownloadTask *downloadTask = [self.manager downloadTaskWithResumeData:resumeData progress:^(NSProgress * _Nonnull downloadProgress) {
            __strong typeof (weakSelf) self = weakSelf;
            self.progressDictionary[@(self.task.tsArr.count - self.tsWaitingDownloadFiles.count)] = downloadProgress;
        } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
            return tsLocalURL;
        } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
            //下载完成
            
        }];
        [downloadTask resume];
        self.currentRequest = downloadTask;
    }else{
        //如果不支持断点下载
        NSURLRequest *request = [NSURLRequest requestWithURL:tsURL];
        __weak typeof(self) weakSelf = self;
        NSURLSessionDownloadTask *downloadTask = [self.manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
            __strong typeof (weakSelf) self = weakSelf;
            self.progressDictionary[@(self.task.tsArr.count - self.tsWaitingDownloadFiles.count)] = downloadProgress;
        } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
            return tsLocalURL;
        } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
            //下载完成
            
        }];
        [downloadTask resume];
        self.currentRequest = downloadTask;
    }
}

- (void)tsDownloadFinishWithUrl:(NSURL*)tsURL tsLocalTempURL:(NSURL*)tsLocalTempURL response:(NSURLResponse *)response error:(NSError*)error{
    
    NSURL *workDir = [self.workSpace URLByAppendingPathComponent:self.task.uri];
    if (error) {
        //获取已经下载的数据
        id taskResumeData = [error.userInfo objectForKey:@"NSURLSessionDownloadTaskResumeData"];
        if ([taskResumeData isKindOfClass:[NSData class]]) {
            NSData *resumeData = (NSData*)taskResumeData;
            if (resumeData.length > 0) {
                if ([self.fileManager fileExistsAtPath:tsLocalTempURL.path]) {
                    [self.fileManager removeItemAtURL:tsLocalTempURL error:nil];
                }
                [self.fileManager createFileAtPath:tsLocalTempURL.path contents:nil attributes:nil];
                NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:tsLocalTempURL.path];
                [fileHandle writeData:resumeData];
                [fileHandle closeFile];
            }
        }
        
        //如果下载失败
        if (error.code == -999) {
            //请求被取消
            self.downloadRetries = 0;
        }else if(error.code == -1011){
            //请求的地址不存在
            self.downloadRetries = 0;
            //删除缓存目录
            NSURL *filem3u8Url = [workDir URLByAppendingPathComponent:@"file.m3u8"];
            NSURL *m3u8DataUrl = [workDir URLByAppendingPathComponent:@"m3u8"];
            [self.fileManager removeItemAtURL:filem3u8Url error:&error];
            [self.fileManager removeItemAtURL:m3u8DataUrl error:&error];
            //提示错误
            if (self.downloadFailureBlock != nil) {
                error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLETsFileUrlInvalid userInfo:@{NSLocalizedDescriptionKey:@"ts文件的下载路径失效或者不存在"}];
                __weak typeof(self) weakSelf = self;
                dispatch_main_async_safe(^{
                    __strong typeof(weakSelf)self = weakSelf;
                    self.downloadFailureBlock(self.task,error);
                    [self pause];
                });
            }else{
                [self pause];
            }
            MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, self.task.mutableCopy)
        }else{
            if (self.downloadRetries >= self.task.downloadRetries) {
                self.downloadRetries = 0;
                if (self.downloadFailureBlock != nil) {
                    error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLETsFileDownloadError userInfo:@{NSLocalizedDescriptionKey:@"ts文件下载失败"}];
                    __weak typeof(self) weakSelf = self;
                    dispatch_main_async_safe(^{
                        __strong typeof(weakSelf)self = weakSelf;
                        self.downloadFailureBlock(self.task,error);
                        [self pause];
                    });
                }else{
                    [self pause];
                }
                MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, self.task.mutableCopy)
            }else{
                self.downloadRetries++;
                self.currentRequest = nil;
                [self.tsWaitingDownloadFiles insertObject:tsURL atIndex:0];
                [self downloadNextFile];
            }
        }
    }else{
        //下载完成
        self.downloadRetries = 0;
        [self.fileManager removeItemAtURL:tsLocalTempURL error:nil];
        if (self.tsWaitingDownloadFiles.count > 0) {
            [self downloadNextFile];
        }else{
            [self allDownloadsDidFinished];
        }
    }
}

-(void)allDownloadsDidFinished{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self timerFire];
        [self destroyTimer];
    });
    
    self.operationQueue.suspended = NO;
    if (self.downloadSuccessBlock != nil) {
        __weak typeof(self) weakSelf = self;
        dispatch_main_async_safe(^{
            __strong typeof(weakSelf)self = weakSelf;
            self.downloadSuccessBlock(self.task);
        });
    }
    MAIN_POST_NOTIFICATION(HLEDownloadCompleteNotice, self.task.mutableCopy)
}
@end
